package com.java.star.mall.front.service.impl;

import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.java.star.mall.common.constant.cache.OrderCacheConstant;
import com.java.star.mall.common.constant.data.OrderConstant;
import com.java.star.mall.common.constant.data.ShowStatusConstant;
import com.java.star.mall.common.exception.BusinessException;
import com.java.star.mall.common.response.JsonPage;
import com.java.star.mall.common.response.StatusCode;
import com.java.star.mall.common.utils.Asserts;
import com.java.star.mall.front.dao.cache.OrderRepository;
import com.java.star.mall.front.dao.mapper.OrderItemMapper;
import com.java.star.mall.front.dao.mapper.OrderMapper;
import com.java.star.mall.front.pojo.authentication.CurrentPrincipal;
import com.java.star.mall.front.pojo.entity.Order;
import com.java.star.mall.front.pojo.entity.OrderItem;
import com.java.star.mall.front.pojo.param.AckOrderParam;
import com.java.star.mall.front.pojo.param.OrderParam;
import com.java.star.mall.front.pojo.param.OrderQueryParam;
import com.java.star.mall.front.pojo.vo.*;
import com.java.star.mall.front.service.AddressService;
import com.java.star.mall.front.service.CartService;
import com.java.star.mall.front.service.OrderService;
import com.java.star.mall.front.service.ProductService;
import com.java.star.mall.front.util.CurrentPrincipalUtils;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.stream.Collectors;

/**
 * 订单Service实现类
 */
@Service
@Slf4j
public class OrderServiceImpl implements OrderService {

    @Autowired
    private AddressService addressService;

    @Autowired
    private CartService cartService;

    @Autowired
    private ProductService productService;

    @Autowired
    private OrderRepository orderRepository;

    @Autowired
    private ThreadPoolExecutor executor;

    @Autowired
    private OrderMapper orderMapper;

    @Autowired
    private OrderItemMapper orderItemMapper;

    @Autowired
    private RedissonClient redissonClient;

    @Override
    public AckOrderInfoVO getAckOrderInfo(AckOrderParam ackOrderParam) {
        AckOrderInfoVO ackOrderInfoVO = new AckOrderInfoVO();

        //获取当前登录的用户信息
        CurrentPrincipal currentPrincipal = CurrentPrincipalUtils.getCurrentPrincipal();

        //查询用户收货地址
        CompletableFuture<Void> addressFuture = getAddressCompletableFuture(ackOrderInfoVO, currentPrincipal);

        //生成订单项并计算数量和总价
        CompletableFuture<Void> orderItemFuture = generateOrderItemCompletableFuture(ackOrderParam, ackOrderInfoVO, currentPrincipal);

        //设置订单令牌保证幂等性，幂等性是为了保证订单不被重复提交
        CompletableFuture<Void> tokenFuture = generateTokenCompletableFuture(ackOrderInfoVO, currentPrincipal);

        try {
            //等待异步执行的结果
            CompletableFuture.allOf(addressFuture, orderItemFuture, tokenFuture).get();
        } catch (Exception e) {
            //如果发生的异常能强转成BusinessException，证明是异步线程出现了业务异常
            if (e instanceof BusinessException) {
                throw (BusinessException) e;
            }
            throw new BusinessException(StatusCode.FAILED);
        }
        return ackOrderInfoVO;
    }

    /**
     * 获取用户收货地址
     *
     * @param ackOrderInfoVO   订单确认VO
     * @param currentPrincipal 当前用户
     * @return 异步结果
     */
    private CompletableFuture<Void> getAddressCompletableFuture(AckOrderInfoVO ackOrderInfoVO, CurrentPrincipal currentPrincipal) {
        CompletableFuture<Void> addressFuture = CompletableFuture.runAsync(() -> {
            AddressVO defaultAddress = addressService.getDefaultAddress(currentPrincipal.getId());
            ackOrderInfoVO.setAddress(defaultAddress);
        }, executor);
        return addressFuture;
    }

    /**
     * 生成订单项并计算数量和总价
     *
     * @param ackOrderParam    订单确认参数
     * @param ackOrderInfoVO   订单确认VO
     * @param currentPrincipal 当前用户
     * @return 异步结果
     */
    private CompletableFuture<Void> generateOrderItemCompletableFuture(AckOrderParam ackOrderParam, AckOrderInfoVO ackOrderInfoVO, CurrentPrincipal currentPrincipal) {
        CompletableFuture<Void> orderItemFuture = CompletableFuture.runAsync(() -> {
            List<OrderItemVO> orderItemVOList = new ArrayList<>();
            //如果是购物车下单，查询当前用户购物车的商品，并找到是选中状态的商品
            if (OrderConstant.ORDER_TYPE_BUY_CART.equals(ackOrderParam.getOrderType())) {
                List<CartVO> cartVOList = cartService.listCheckedItem(currentPrincipal.getId());
                orderItemVOList = cartVOList.stream().map(cartVO -> {
                    OrderItemVO orderItemVO = new OrderItemVO();
                    BeanUtils.copyProperties(cartVO, orderItemVO);
                    return orderItemVO;
                }).collect(Collectors.toList());
            }

            //如果是立即购买
            if (OrderConstant.ORDER_TYPE_BUY_NOW.equals(ackOrderParam.getOrderType())) {
                ProductDetailVO productDetail = productService.getProductDetail(ackOrderParam.getProductId());
                OrderItemVO orderItemVO = new OrderItemVO();
                BeanUtils.copyProperties(productDetail, orderItemVO);
                //立即购买的商品数量默认1
                orderItemVO.setNum(1);
                orderItemVOList.add(orderItemVO);
            }

            //计算总价和数量
            BigDecimal total = new BigDecimal("0.0");
            int count = 0;
            for (OrderItemVO orderItemVO : orderItemVOList) {
                BigDecimal multiply = orderItemVO.getPrice()
                        .multiply(new BigDecimal(orderItemVO.getNum().toString()));
                total = total.add(multiply);
                count = count + orderItemVO.getNum();
            }

            //装填数据
            ackOrderInfoVO.setOrderItemList(orderItemVOList);
            ackOrderInfoVO.setTotal(total);
            ackOrderInfoVO.setCount(count);
        }, executor);
        return orderItemFuture;
    }

    /**
     * 生成防重令牌
     *
     * @param ackOrderInfoVO   订单确认VO
     * @param currentPrincipal 当前用户
     * @return 异步结果
     */
    private CompletableFuture<Void> generateTokenCompletableFuture(AckOrderInfoVO ackOrderInfoVO, CurrentPrincipal currentPrincipal) {
        CompletableFuture<Void> tokenFuture = CompletableFuture.runAsync(() -> {
            String orderToken = UUID.randomUUID().toString().replace("-", "");
            orderRepository.saveOrderToken(currentPrincipal.getId(), orderToken);
            ackOrderInfoVO.setOrderToken(orderToken);
        }, executor);
        return tokenFuture;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public SubmitOrderVO submitOrder(OrderParam orderParam) {
        //获取当前用户对象
        CurrentPrincipal currentPrincipal = CurrentPrincipalUtils.getCurrentPrincipal();

        //效验订单防重令牌
        boolean validateToken = orderRepository.validateToken(currentPrincipal.getId(), orderParam.getOrderToken());
        Asserts.isTrue(!validateToken, StatusCode.APP_ERROR, "订单重复提交");

        //构建订单
        Order order = buildOrder(orderParam, currentPrincipal);

        //构建订单项
        List<OrderItem> orderItemList = buildOrderItem(orderParam, order.getOrderSn());

        //构建金额
        buildPrice(order, orderItemList);

        //验价
        boolean validatePrice = validatePrice(orderParam, order);
        Asserts.isTrue(!validatePrice, StatusCode.APP_ERROR, "价格有变动，请重新下单");

        //保存订单
        saveOrder(order, orderItemList);

        //扣减库存
        reduceStock(orderItemList);

        //清除购物车
        cleanCart(orderItemList, orderParam);

        SubmitOrderVO submitOrderVO = new SubmitOrderVO();
        submitOrderVO.setOrderSn(order.getOrderSn());
        submitOrderVO.setPayAmount(order.getPayAmount());
        return submitOrderVO;
    }

    /**
     * 构建订单
     *
     * @param orderParam       订单参数
     * @param currentPrincipal 当前登录的用户
     * @return 订单实体
     */
    private Order buildOrder(OrderParam orderParam, CurrentPrincipal currentPrincipal) {
        Order order = new Order();
        //设置订单号
        String orderSn = orderRepository.generateID();
        order.setOrderSn(orderSn);

        //设置订单初始状态
        order.setStatus(OrderConstant.ORDER_STATUS_CREATE_NEW);
        order.setShowStatus(ShowStatusConstant.SHOW);

        //设置订单时间
        Date now = new Date();
        order.setGmtCreate(now);
        order.setGmtUpdate(now);

        //设置用户信息及地址
        AddressVO address = addressService.getAddress(orderParam.getAddressId());
        Asserts.isTrue(Objects.isNull(address), StatusCode.APP_ERROR, "收货地址不存在");
        order.setUserId(currentPrincipal.getId());
        order.setReceiverName(address.getName());
        order.setReceiverPhone(address.getPhone());
        order.setReceiverProvince(address.getProvince());
        order.setReceiverCity(address.getCity());
        order.setReceiverDistrict(address.getDistrict());
        order.setReceiverTown(address.getTown());
        order.setReceiverLandmark(address.getLandmark());

        return order;
    }

    /**
     * 构建订单项
     *
     * @param orderParam 订单参数
     * @param orderSn    订单号
     * @return 订单项实体列表
     */
    private List<OrderItem> buildOrderItem(OrderParam orderParam, String orderSn) {
        //查询订单项，并构建
        List<OrderItem> orderItemList = orderParam.getOrderItems().stream().map(orderItemParam -> {
            ProductDetailVO productDetail = productService.getProductDetail(orderItemParam.getId());
            Asserts.isTrue(Objects.isNull(productDetail), StatusCode.APP_ERROR, "商品id:" + orderItemParam.getId() + "，不存在");
            OrderItem orderItem = new OrderItem();
            orderItem.setOrderSn(orderSn);
            orderItem.setProId(productDetail.getId());
            orderItem.setProName(productDetail.getName());
            orderItem.setProPrice(productDetail.getPrice());
            orderItem.setProPic(productDetail.getProPic());
            orderItem.setProNum(orderItemParam.getNum());
            orderItem.setShowStatus(ShowStatusConstant.SHOW);
            Date now = new Date();
            orderItem.setGmtCreate(now);
            orderItem.setGmtUpdate(now);
            return orderItem;
        }).collect(Collectors.toList());

        return orderItemList;
    }

    /**
     * 构建金额
     *
     * @param order         订单实体
     * @param orderItemList 订单项
     */
    private void buildPrice(Order order, List<OrderItem> orderItemList) {
        //TODO 目前只构建应付价格，未来可能还会有优惠金额，运费，积分等
        BigDecimal payAmount = new BigDecimal("0.0");
        for (OrderItem orderItem : orderItemList) {
            BigDecimal currentItemTotal = orderItem.getProPrice()
                    .multiply(new BigDecimal(orderItem.getProNum().toString()));
            payAmount = payAmount.add(currentItemTotal);
        }
        order.setPayAmount(payAmount);
    }


    /**
     * 验价
     *
     * @param orderParam 订单参数
     * @param order      订单实体
     * @return true：合法  false：不合法
     */
    private boolean validatePrice(OrderParam orderParam, Order order) {
        //价格差距在0.01以内我们认为价格是一致的
        return Math.abs(orderParam.getTotal()
                .subtract(order.getPayAmount()).doubleValue()) < 0.01;
    }

    /**
     * 保存订单
     *
     * @param order         订单实体
     * @param orderItemList 订单项实体
     */
    private void saveOrder(Order order, List<OrderItem> orderItemList) {
        //保存订单，并拿到自增id
        orderMapper.insert(order);
        Integer orderId = order.getId();
        log.debug("订单号：{}", orderId);

        //保存订单项信息
        orderItemList = orderItemList.stream().peek(orderItem -> orderItem.setOrderId(orderId))
                .collect(Collectors.toList());
        orderItemMapper.batchInsert(orderItemList);
    }

    /**
     * 扣减库存
     *
     * @param orderItemList 订单项列表
     */
    private void reduceStock(List<OrderItem> orderItemList) {
        RLock rlock = redissonClient.getLock(OrderCacheConstant.ORDER_STOCK_LOCK);
        try {
            //加锁，防止超卖
            rlock.lock();
            for (OrderItem orderItem : orderItemList) {
                ProductDetailVO productDetail = productService.getProductDetail(orderItem.getProId());
                if (productDetail.getStock() <= 0) {
                    throw new BusinessException(StatusCode.APP_ERROR, "商品:" + orderItem.getProName() + "，库存不足");
                }
                //减库存
                productService.reduceStock(orderItem.getProId(), orderItem.getProNum());
            }
        } finally {
            rlock.unlock();
        }
    }

    /**
     * 清除购物车
     *
     * @param orderItemList 商品项列表
     * @param orderParam    订单参数
     */
    private void cleanCart(List<OrderItem> orderItemList, OrderParam orderParam) {
        //当购买方式是购物车购买时才考虑购物车清除操作
        if (OrderConstant.ORDER_TYPE_BUY_CART.equals(orderParam.getOrderType())) {
            //只删除用户已经下单的购物车项
            for (OrderItem orderItem : orderItemList) {
                cartService.deleteToCart(orderItem.getProId());
            }
        }
    }

    @Override
    public OrderVO getOrderByOrderSn(String orderSn) {
        OrderVO orderVo = new OrderVO();
        Order order = orderMapper.getOrderByOrderSn(orderSn);
        BeanUtils.copyProperties(order, orderVo);
        return orderVo;
    }

    @Override
    public JsonPage<OrderListVO> listOrder(OrderQueryParam orderQueryParam) {
        //获取当前用户
        CurrentPrincipal currentPrincipal = CurrentPrincipalUtils.getCurrentPrincipal();
        //分页查询用户订单
        Page<Object> p = PageHelper.startPage(orderQueryParam.getPage(), orderQueryParam.getPageSize());
        List<Order> orderList = orderMapper.listByUserId(currentPrincipal.getId(), orderQueryParam.getStatus());

        //装填数据
        List<OrderListVO> orderListVOList = orderList.stream().map(order -> {
            OrderListVO orderListVO = new OrderListVO();
            BeanUtils.copyProperties(order, orderListVO);
            //查询每个订单的订单项
            List<OrderItem> orderItemList = orderItemMapper.listByOrderId(order.getId());
            List<OrderItemVO> orderItemVOList = orderItemList.stream().map(orderItem -> {
                OrderItemVO orderItemVO = new OrderItemVO();
                orderItemVO.setId(orderItem.getProId());
                orderItemVO.setName(orderItem.getProName());
                orderItemVO.setPrice(orderItem.getProPrice());
                orderItemVO.setNum(orderItem.getProNum());
                orderItemVO.setProPic(orderItem.getProPic());
                return orderItemVO;
            }).collect(Collectors.toList());
            orderListVO.setOrderItemList(orderItemVOList);
            return orderListVO;
        }).collect(Collectors.toList());

        //装填分页数据
        PageInfo<OrderListVO> pageInfo = new PageInfo<>(orderListVOList);
        pageInfo.setTotal(p.getTotal());
        pageInfo.setPages(p.getPages());
        pageInfo.setPageNum(p.getPageNum());
        pageInfo.setPageSize(p.getPageSize());
        return JsonPage.restPage(pageInfo);
    }

}
